﻿using System;
using System.ComponentModel;
using System.Linq;

namespace DeepEarth.Toolkit.Controls
{
    public enum MeasurementSystem
    {
        Metric,
        Imperial
    }

    public abstract class Measurement : INotifyPropertyChanged
    {
        protected Tuple<string, double>[] units;

        public Measurement(Tuple<string, double>[] units)
        {
            this.units = units;
        }

        #region Properties
        double rawValue;
        public double RawValue
        {
            get { return rawValue; }
            set
            {
                if (rawValue != value)
                {
                    rawValue = value;
                    OnPropertyChanged("RawValue", "Value", "Description");
                }
            }
        }

        public double Value
        {
            get { return RawValue * UnitRatio; }
        }

        string unit;
        public string Unit
        {
            get { return unit; }
            set
            {
                if (value != unit)
                {
                    unit = value;
                    OnPropertyChanged("Unit", "Description");
                }
            }
        }

        public virtual string Description
        {
            get { return Value.ToString("F2") + Unit; }
        }

        MeasurementSystem measurementSystem;
        public MeasurementSystem MeasurementSystem
        {
            get { return measurementSystem; }
            set
            {
                if (value != measurementSystem)
                {
                    measurementSystem = value;
                    OnPropertyChanged("MeasurementSystem", "Value", "Unit", "Description");
                }
            }
        }

        double unitRatio;
        public double UnitRatio
        {
            get { return unitRatio; }
            set
            {
                if (value != unitRatio)
                {
                    unitRatio = value;
                    OnPropertyChanged("UnitRatio", "Value", "Description");
                }
            }
        }
        #endregion

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(params string[] propertyNames)
        {
            if (PropertyChanged != null)
                foreach (var propertyName in propertyNames)
                {
                    switch (propertyName)
                    {
                        case "Unit":
                            UnitRatio = units.First(u => u.Item1 == Unit).Item2;
                            break;
                        case "Value":
                        case "MeasurementSystem":
                            Unit = CalculateBestUnit() ?? units[0].Item1;
                            break;
                    }

                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
        }

        protected abstract string CalculateBestUnit();
    }

    public class DistanceMeasurement : Measurement
    {
        public DistanceMeasurement() : base(new[]
            {
                Tuple.Create("m", 1d),
                Tuple.Create("km", 0.001),
                Tuple.Create("Mi", 0.000621371192),
                Tuple.Create("yd", 1.0936133)
            }) { }

        protected override string CalculateBestUnit()
        {
            switch (MeasurementSystem)
            {
                case MeasurementSystem.Metric:
                    return RawValue > 1000 ? "km" : "m";
                case MeasurementSystem.Imperial:
                    return RawValue > 1760 ? "Mi" : "yd";
            }
            return null;
        }
    }

    public class AreaMeasurement : Measurement
    {
        public AreaMeasurement() : base(new[]
            {
                Tuple.Create("ha", 0.0001),
                Tuple.Create("ac", 0.000247105381),
            }) { }

        protected override string CalculateBestUnit()
        {
            switch (MeasurementSystem)
            {
                case MeasurementSystem.Metric:
                    return "ha";
                case MeasurementSystem.Imperial:
                    return "ac";
            }
            return null;
        }

        public override string Description
        {
            get { return double.IsNaN(Value) ? "" : Value.ToString("F2") + Unit; }
        }
    }
}